package wj.tree;

import java.util.ArrayList;

import wj.WJValue;

public class ExprNode implements WJNode {

	public static final int OR = 0;
	public static final int AND = 1;
	public static final int EQUAL = 2;
	public static final int NOT_EQUAL = 3;
	public static final int GREATER_EQUAL = 4;
	public static final int LESSER_EQUAL = 5;
	public static final int GREATER = 6;
	public static final int LESSER = 7;
	public static final int ADD = 8;
	public static final int SUB = 9;
	public static final int MULT = 10;
	public static final int DIV = 11;
	public static final int MOD = 12;
	public static final int POW = 13;
	public static final int NEG = 14;
	public static final int NOT = 15;
	public static final int NOTHING = 16;
	
	private ArrayList<WJNode> operands;
	private int mode;
	
	private ArrayList<Integer> operations;
	
	public ExprNode(){
		operands = new ArrayList<WJNode>();
		operations = new ArrayList<Integer>();
	}
	
	public ExprNode(WJNode a, int mode){
		this();
		operands.add(a);
		this.mode = mode;
	}
	
	public ExprNode(WJNode a, WJNode b, int mode){
		this(a,mode);
		operands.add(b);
	}
	
	public void add(Object a){
		operands.add((WJNode)a);
	}
	
	public void setMode(int mode){
		this.mode = mode;
	}

	public void addOp(int opMode){
		if(opMode < 0 && opMode > 16)
			opMode = 16;
		
		operations.add(opMode);
		
	}
	
	@Override
	public WJValue evaluate() {
		
		WJValue val = WJValue.NULL;
		
		if(mode == MULT || mode == DIV || mode == MOD){ //assume all in the arraylist has one of these ops
			for(int i=0;i<operands.size();i++){
				WJNode operand  = operands.get(i);
				int currOp = 0;
				if(i > 0)
					currOp= operations.get(i-1);
				if(val == WJValue.NULL)
					val = new WJValue(operand.evaluate());
				else{
					switch(currOp){
						case MULT: 
							val = new WJValue(val.toDouble() * operand.evaluate().toDouble());
							break;
							
						case DIV:
							val = new WJValue(val.toDouble() / operand.evaluate().toDouble());
							break;
							
						case MOD:
							val = new WJValue(val.toDouble() % operand.evaluate().toDouble());
							break;
					}
				}
			}			
		}
		else if(mode == ADD || mode == SUB){
			for(int i=0;i<operands.size();i++){
				WJNode operand  = operands.get(i);
				int currOp = 0;
				if(i > 0)
					currOp= operations.get(i-1);
				if(val == WJValue.NULL)
					val = new WJValue(operand.evaluate());
				else{
					switch(currOp){
						case ADD: 
							val = new WJValue(val.toDouble() + operand.evaluate().toDouble());
							break;
							
						case SUB:
							val = new WJValue(val.toDouble() - operand.evaluate().toDouble());
							break;
					}
				}
			}			
			
		}
		else{
			switch(mode){
				case OR:
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toBoolean() || node.evaluate().toBoolean());
					break;
					
				case AND: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toBoolean() && node.evaluate().toBoolean());
					break;
					
				case EQUAL:  
					for(WJNode node : operands){
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toString().equals(node.evaluate().toString()));
					}
					break;
			
				case NOT_EQUAL: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(!val.toString().equals(node.evaluate().toString()));
					break;
					
				case GREATER_EQUAL: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toDouble() >= node.evaluate().toDouble());
					break;
				
				case LESSER_EQUAL: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toDouble() <= node.evaluate().toDouble());
					break;
					
				case GREATER: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toDouble() > node.evaluate().toDouble());
					break;
					
				case LESSER: 
					for(WJNode node : operands)
						if(val == WJValue.NULL)
							val = new WJValue(node.evaluate());
						else
							val = new WJValue(val.toDouble() < node.evaluate().toDouble());
					break;
								
				case POW:
					if(operands.size() > 0){
						val = new WJValue(operands.get(operands.size()-1).evaluate());
						
						if(operands.size() > 1){
							for(int i= operands.size() - 2; i >=0; i--)
								val = new WJValue(Math.pow(operands.get(i).evaluate().toDouble(), val.toDouble()));
						}
					}
					break;
					
				case NEG: val = new WJValue(-1 * operands.get(0).evaluate().toDouble());	break;
				case NOT: val = new WJValue(!operands.get(0).evaluate().toBoolean());	break;
				case NOTHING: 
					if(operands.size() == 1)
						val = operands.get(0).evaluate();
					break;
				default: {	System.out.println("Evaluated no mode ."+mode);return WJValue.NULL;}
			}
			
		}

		//System.out.println("Evaluated mode "+mode+" result "+val.toString());
		return val;
	}

}
